package model;

import model.User.UserTypes;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import util.Stringer;

/**
 * The System Class.
 * @author Andy Johnson
 */
public class System {
	private static Random randGen = 
		new Random(java.lang.System.currentTimeMillis());
	
	/** 
	 * The Users instance. 
	 */
	private  Users myUsers;
	
	/** 
	 * The jobs instance. 
	 */
	private Jobs myJobs;
	
	/** The my current user. */
	transient private User myCurrentUser;
	
	/** The my current user type. */
	transient private User.UserTypes myCurrentUserType;
	
	/**
	 * Instantiates a new system.
	 */
	public System()
	{
		myUsers = new Users();
		myJobs = new Jobs();
		myCurrentUserType = User.UserTypes.Unregistered;
		myCurrentUser = null;
		
		Deserialize();
	}
	
	/**
	 * Instantiates a new system for testing purposes.
	 */
	public System(final boolean doNotSerialize)
	{
		myUsers = new Users();
		myJobs = new Jobs();
		myCurrentUserType = User.UserTypes.Unregistered;
		myCurrentUser = null;
	}
	
	
	/**
	 * Gets the users object instance.
	 *
	 * @return the users
	 */
	public Users getUsers()
	{
		return myUsers;
	}
	
	/**
	 *  Sets the myUsers object instance.
	 * @param theUsers the new instance to set myUsers too.
	 */
	public void setUsers(final Users theUsers)
	{
		myUsers = theUsers;
	}
	
	/**
	 * Gets the jobs instance.
	 *
	 * @return the current instance of the Jobs class.
	 */
	public Jobs getJobs()
	{
		return myJobs;
	}
	
	/**
	 * Sets the jobs.
	 *
	 * @param theJobs sets the instance of the Jobs class.
	 */
	public void setJobs(final Jobs theJobs)
	{
		myJobs = theJobs;
	}
	
	
	/**
	 * Gets the current user.
	 *
	 * @return the current user
	 */
	public User getCurrentUser()
	{
		return myCurrentUser;
	}
	
	/**
	 * Gets the current user type.
	 *
	 * @return the current user type
	 */
	public UserTypes getCurrentUserType()
	{
		return myCurrentUserType;
	}
	
	/**
	 * Login a user to the System.
	 *
	 * @param theUsername the username logging in.
	 * @param thePassword the password of the login attempt.
	 * @return true, if the login is successful
	 */
	public boolean login(final String theUsername, 
								final String thePassword)
	{
		User tempUser = myUsers.findUser(theUsername);

		if(tempUser == null)
		{
			return false;
		}
		
		if(tempUser.authenticate(thePassword))
		{
			myCurrentUser = tempUser;
			if(myCurrentUser instanceof Volunteer)
			{
				myCurrentUserType = UserTypes.Volunteer;
			}
			else if (myCurrentUser instanceof LMA)
			{
				myCurrentUserType = UserTypes.LMA;
			}
			else if (myCurrentUser instanceof Scheduler)
			{
				myCurrentUserType = UserTypes.Scheduler;
			}
			return true;
		}
		else
		{
			myCurrentUserType = UserTypes.Unregistered;
			return false;
		}
	}
	
	/**
	 * Register.
	 *
	 * @param theVol the the vol
	 * @return true, if successful
	 */
	public boolean register(final Volunteer theVol)
	{
		if(myCurrentUserType == UserTypes.Unregistered){
			return myUsers.addVolunteer(theVol);
		}
		else{
			// only unregistered can register to be vol
			return false; 
		}
		
	}
	
	/**
	 * Register.
	 *
	 * @param theLMA the the lma
	 * @return true, if successful
	 */
	public boolean register(final LMA theLMA)
	{
		if(myCurrentUserType == UserTypes.Unregistered){
			return myUsers.addLMA(theLMA);
		}
		else{
			// only unregistered can register to be vol
			return false; 
		}
		
	}
	
	/**
	 * Gets the all jobs.
	 *
	 * @return the all jobs
	 */
	public HashMap<String, Job> getAllJobs()
	{
		return myJobs.getAllJobs();
	}
	
	/**
	 * Gets the all requests.
	 *
	 * @return the all requests
	 */
	public HashMap<String, Job> getAllRequests()
	{
		if(myCurrentUserType == UserTypes.Unregistered){
			return null; //unregistered users can't see this
		}
		else{
			return myJobs.getRequestedJobs();
		}
	}
	
	/**
	 * Adds the request.
	 *
	 * @param theJob the the job
	 * @return true, if successful
	 */
	public boolean addRequest(final Job theJob)
	{
		if(myCurrentUserType == UserTypes.LMA){
			return myJobs.addNewRequest(theJob);
		}
		else{
			return false;
		}
	}
	
	/**
	 * Gets the scheduler info.
	 *
	 * @return the scheduler info
	 */
	public String getSchedulerInfo()
	{
		Scheduler sched = myUsers.getScheduler();
		return sched.getFirstName() + " " + 
			   sched.getLastName() + "\n" + 
			   sched.getEmail() + "\n" + 
			   sched.getPhoneNumber();
	}
	
	/**
	 * Adds a scheduler to the users.
	 *
	 * @param aScheduler the a scheduler
	 * @return true, if successful
	 */
	public boolean addScheduler(final Scheduler aScheduler)
	{
		return myUsers.addScheduler(aScheduler);
	}
	
	/**
	 * Adds a lma to the users.
	 *
	 * @param aLMA the a lma
	 * @return true, if successful
	 */
	public boolean addLMA(final LMA aLMA)
	{
		return myUsers.addLMA(aLMA);
	}
	
	/**
	 * Adds a volunteer to the users.
	 *
	 * @param aVolunteer the a volunteer
	 * @return true, if successful
	 */
	public boolean addVolunteer(final Volunteer aVolunteer)
	{
		return myUsers.addVolunteer(aVolunteer);
	}
	
	public void Deserialize()
	{
		File userStorage = new File(Stringer.getString(0));
		
		if(userStorage.exists())
		{
			try {
				FileInputStream fis = 
					new FileInputStream(userStorage);
				ObjectInputStream ois = 
					new ObjectInputStream(fis);
				myUsers = (Users) ois.readObject();
				ois.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else
		{
			// generate random default values for the users
			try {
				FileReader userFR = 
					new FileReader(Stringer.getString(2));
				BufferedReader br = 
					new BufferedReader(userFR);
				
				String line = "";
				while((line = br.readLine()) != null)
				{
					String[] vals = line.split(":");
					
					if(vals[0].equalsIgnoreCase("Volunteer"))
					{
						Volunteer newVol = 
							new Volunteer(vals[1], vals[2], 
									vals[3], vals[4], 
									vals[5], vals[6], 
									vals[7]);
						if(vals[8].equalsIgnoreCase("GL"))
						{
							newVol.setGLStatus(true);
						}
						addVolunteer(newVol);
					}
					else if(vals[0].equalsIgnoreCase("LMA"))
					{
						LMA newLMA = new LMA(vals[1], 
								vals[2], vals[3], vals[4], 
								vals[5], vals[6], vals[7], 
								vals[8], myJobs);
						newLMA.setVerified(true);
						addLMA(newLMA);
					}
					else if(vals[0].
							equalsIgnoreCase("Scheduler"))
					{
						Scheduler newScheduler = 
							new Scheduler(vals[1], vals[2], 
									vals[3], vals[4], 
									vals[5], vals[6], 
									vals[7], myJobs, 
									myUsers);
						addScheduler(newScheduler);
					}
				}
			} catch (FileNotFoundException e) {
				//
				e.printStackTrace();
			} catch (IOException e) {
				// 
				e.printStackTrace();
			}
		}
		
		File JobStorage = new File(Stringer.getString(1));
		
		if(JobStorage.exists())
		{
			try {
				FileInputStream fis = 
					new FileInputStream(JobStorage);
				ObjectInputStream ois = 
					new ObjectInputStream(fis);
				myJobs = (Jobs) ois.readObject();
				ois.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		else
		{
			try {
                FileReader jobFR = 
                	new FileReader(Stringer.getString(3));
                BufferedReader br = 
                	new BufferedReader(jobFR);
                
                String line = "";
                int i = 0;
                while((line = br.readLine()) != null)
                {
                        String[] vals = line.split(":");
                        ArrayList<LMA> someLMAs = 
                        	myUsers.getLMAs();
                        Job newJob = new Job(vals[0], 
                        	randGen.nextInt(
                        			Integer.parseInt(
                        					Stringer.
                        					getString(4))) +
                        					1, 
                        	randGen.nextInt(
                        			Integer.parseInt(
                        					Stringer.
                        					getString(5))) +
                        					1, 
                        	someLMAs.get(
                        		randGen.nextInt(
                        				someLMAs.size() - 1)
                        				+ 1), 
                        		randGen.nextInt(
                        			Integer.parseInt(
                        					Stringer.
                        					getString(6))) +
                        					1, 
                        		randGen.nextInt(
                        				Integer.parseInt(
                        					Stringer.
                        					getString(7))) +
                        					1, 
                        		vals[1]);
                        myJobs.addNewRequest(newJob);
                        if(i == 0)
                        {
                        	myJobs.scheduleJob(newJob, 
                        			Integer.parseInt(
                        					Stringer.
                        					getString(8)),
                        			Integer.parseInt(
                        					Stringer.
                        					getString(9)), 
                        			Integer.parseInt(
                        					Stringer.
                        					getString(10)), 
                        			Integer.parseInt(
                        					Stringer.
                        					getString(11)), 
                        			Integer.parseInt(
                        					Stringer.
                        					getString(12)), 
                        			Integer.parseInt(
                        					Stringer.
                        					getString(13)));
                        }
                        
                        i++;
                }
	        } catch (FileNotFoundException e) {
	                //
	                //e.printStackTrace();
	        } catch (IOException e) {
	                // 
	                //e.printStackTrace();
	        }
		}
	}
	
	public void Serialize()
	{
		File userStorage = new File(Stringer.getString(0));
		
		try {
			FileOutputStream fos = 
				new FileOutputStream(userStorage);
			ObjectOutputStream oos = 
				new ObjectOutputStream(fos);
			oos.writeObject(myUsers);
			oos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		File jobStorage = new File(Stringer.getString(1));
		
		try {
			FileOutputStream fos = 
				new FileOutputStream(jobStorage);
			ObjectOutputStream oos = 
				new ObjectOutputStream(fos);
			oos.writeObject(myJobs);
			oos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
